/**
 * @file fpga_test.c
 * @author 读写FPGA DDR的测试程序。“./fpga_test 5”，表示读写5个单位长度的数据。
 *         一个单位长度为256bit。
 *         长度后面，加上“-v”或“-p”
 *         “-v”在测试失败时打印数据，长度太长时不要使用，否则需要打印很久
 *         “-p”在测试失败时将数据保存在文件里
 * @brief
 * @version 0.1
 * @date 2024-03-22
 *
 * @copyright Copyright (c) 2024
 *
 */

#include "fpga_interface.h"
#include <string.h>
#include <time.h>

#define PROGRAM_NAME "fpga_test"

static void showusage(void) {
    fprintf(stderr, "usage: %1$s <len> <-v or -p>\n", PROGRAM_NAME);
    exit(EXIT_FAILURE);
}

static void hex_dump(const void *src, size_t length) {
    const unsigned char *address = src;
    uint16_t i;

    for (i = 0; i < length; i++) {
        printf("%02X ", address[i]);
        if ((i + 1) % 32 == 0)
            printf("\n");
    }

    printf("\n");
}

static void hex_dump_to_file(const char *filename, const void *src, size_t length) {
    const uint8_t *data = src;
    FILE *fp;
    fp = fopen(filename, "w");
    if (fp == NULL)
        perror("Failed to open file");

    for (size_t i = 0; i < length; ++i) {
        fprintf(fp, "%02x ", data[i]);
        if ((i + 1) % 32 == 0)
            fprintf(fp, "\n");
    }
    fclose(fp);
}

static void swap_u8(uint8_t *a, uint8_t *b) {
    uint8_t temp;
    temp = *a;
    *a = *b;
    *b = temp;
}

int main(int argc, char *argv[]) {
    int ret;
    int verbose_enable = 0; // 是否打印读写数据
    int persistance = 0;    // 是否保存读写数据

    uint8_t *data1, *data2;
    uint32_t len, i, j;
    uint32_t bytes;

    if (argc != 2 && argc != 3)
        showusage();

    if (argc == 3 && (argv[2][0] == '-' && argv[2][1] == 'v'))
        verbose_enable = 1;

    if (argc == 3 && (argv[2][0] == '-' && argv[2][1] == 'p'))
        persistance = 1;

    fpga_init(0);
    set_flowctrl_mode(1); // 进入FMC读写测试模式

    // 读出要测试的长度
    len = strtoll(argv[1], NULL, 0);
    bytes = len * 32; // FMC读写长度寄存器的单位为256bit

    data1 = (uint8_t *)malloc(bytes * sizeof(uint8_t));
    if (data1 == NULL)
        errmsg_die("malloc failed!");

    data2 = (uint8_t *)malloc(bytes * sizeof(uint8_t));
    if (data2 == NULL)
        errmsg_die("malloc failed!");

    // 生成随机测试数据
    srand((unsigned int)time(NULL));
    for (i = 0; i < bytes / 4; ++i) {
        // data1[i] = rand() % 255;
        uint32_t *data3 = (uint32_t *)data1;
        data3[i] = i;
    }

    write_fpga_ddr(0, len, data1);

    read_fpga_ddr(0, len, data2, 0);

    // /* 因为先入先出，调换顺序后，才方便与前面写入的数据对比 */
    for (i = 0; i < len; i++) {
        for (j = 0; j < 4; j++) {
            uint32_t *data3 = (uint32_t *)data2;
            uint32_t temp = data3[i * 8 + j];

            uint8_t *u8_ptr = (uint8_t *)&temp;
            swap_u8(u8_ptr, u8_ptr + 2);
            swap_u8(u8_ptr + 1, u8_ptr + 3);

            data3[i * 8 + j] = data3[i * 8 + 7 - j];
            u8_ptr = (uint8_t *)&data3[i * 8 + j];
            swap_u8(u8_ptr, u8_ptr + 2);
            swap_u8(u8_ptr + 1, u8_ptr + 3);

            data3[i * 8 + 7 - j] = temp;
        }
    }

    for (i = 0; i < len; i++) {
        for (j = 0; j < 8; j++) {
            uint32_t *data3 = (uint32_t *)data1;
            uint32_t *data4 = (uint32_t *)data2;
            if (data3[i * 8 + j] != data4[i * 8 + j])
                printf("Incorect data 1: %d, 2: %d\n", data3[i * 8 + j], data4[i * 8 + j]);
        }
    }

    if (memcmp(data1, data2, bytes) == 0) {
        printf("TEST PASS!\n");
    } else {
        if (verbose_enable) {
            hex_dump(data1, bytes);
            hex_dump(data2, bytes);
        }
        if (persistance) {
            hex_dump_to_file("data1.txt", data1, bytes);
            hex_dump_to_file("data2.txt", data2, bytes);
        }
        printf("TEST FAIL!\n");
    }

    set_flowctrl_mode(0); //  退出FMC读写测试模式
    close_fpga(0);

    return ret;
}
